package org.hepeng.workx.mybatis.interceptor;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;


/**
 * @author he peng
 */
public abstract class AbstractExecutorInvokeInterceptor extends AbstractInterceptor {

    @Override
    public Object plugin(Object target) {
        return Executor.class.isAssignableFrom(target.getClass()) ? Plugin.wrap(target , this) : target;
    }

    protected MappedStatement extractMappedStatement(Invocation invocation) {
        return (MappedStatement) invocation.getArgs()[0];
    }

    protected Executor extractExecutor(Invocation invocation) {
        return (Executor) invocation.getTarget();
    }

    protected Object extractParameterObject(Invocation invocation) {
        return invocation.getArgs()[1];
    }

    protected BoundSql extractBoundSql(Invocation invocation) {
        MappedStatement ms = extractMappedStatement(invocation);
        return ms.getBoundSql(extractParameterObject(invocation));
    }

    protected String extractSql(Invocation invocation) {
        BoundSql boundSql = extractBoundSql(invocation);
        return boundSql.getSql();
    }

    protected MappedStatement copyFromPrototype(String idSuffix, MappedStatement prototypems , List<ResultMap> resultMaps) {
        MappedStatement.Builder builder = new MappedStatement.Builder(prototypems.getConfiguration(),
                prototypems.getId() + idSuffix,
                prototypems.getSqlSource(), prototypems.getSqlCommandType());
        for (Field field : FieldUtils.getAllFieldsList(MappedStatement.class)) {
            try {
                field.setAccessible(true);
                MethodUtils.invokeMethod(builder , true , field.getName() ,
                        getArgs(field.getName() , prototypems) , getParameterTypes(field));
            } catch (Exception e) {
                if (e instanceof NoSuchMethodException && StringUtils.endsWith(field.getName() , "a")) {
                    String methodName = StringUtils.reverse(StringUtils.removeFirst(StringUtils.reverse(field.getName()), "s"));
                    try {
                        MethodUtils.invokeMethod(builder , true , methodName ,
                                getArgs(field.getName() , prototypems) , getParameterTypes(field));
                    } catch (Exception ex) {
                    }
                }
            }
        }
        if (Objects.nonNull(resultMaps)) {
            builder.resultMaps(resultMaps);
        }
        return builder.build();
    }

    private Object[] getArgs(String name , MappedStatement prototypems) throws IllegalAccessException {
        Object[] args;
        Object obj = FieldUtils.readDeclaredField(prototypems , name , true);
        if (! (obj instanceof Object[])) {
            args = new Object[]{obj};
        } else {
            args = (Object[]) obj;
        }
        return args;
    }

    private Class<?>[] getParameterTypes(Field field) {
        return new Class[]{field.getType()};
    }

}
